package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/apache/rocketmq-client-go/v2/consumer"
	"github.com/apache/rocketmq-client-go/v2/primitive"
	goredislib "github.com/go-redis/redis/v8"
	"github.com/go-redsync/redsync/v4"
	"github.com/go-redsync/redsync/v4/redis/goredis/v8"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"gorm.io/gorm"
	"mxshop_srvs/inventory_srv/global"
	"mxshop_srvs/inventory_srv/model"
	"mxshop_srvs/inventory_srv/proto"
)

type InventoryServer struct {
	proto.UnimplementedInventoryServer
}

/*
*
设置库存
*/
func (c *InventoryServer) SetInv(ctx context.Context, req *proto.GoodsInvInfo) (*emptypb.Empty, error) {
	inventory := model.Inventory{}
	global.DB.Where(&model.Inventory{Goods: req.GoodsId}).First(&inventory)
	inventory.Stocks = req.Num
	inventory.Goods = req.GoodsId
	global.DB.Save(&inventory)
	return &emptypb.Empty{}, nil
}

/*
*
获取库存详情
*/
func (c *InventoryServer) InvDetail(ctx context.Context, req *proto.GoodsInvInfo) (*proto.GoodsInvInfo, error) {
	var inventory model.Inventory
	if result := global.DB.Where(&model.Inventory{Goods: req.GoodsId}).First(&inventory); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "没有库存消息")
	}
	goodsInvInfo := proto.GoodsInvInfo{
		GoodsId: inventory.ID,
		Num:     inventory.Stocks,
	}
	return &goodsInvInfo, nil
}

/*
*
扣减库存
*/
func (c *InventoryServer) Sell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {
	client := goredislib.NewClient(&goredislib.Options{
		Addr: fmt.Sprintf("%s:%d", global.ServerConfig.RedisInfo.Host, global.ServerConfig.RedisInfo.Port),
	})
	pool := goredis.NewPool(client) // or, pool := redigo.NewPool(...)
	rs := redsync.New(pool)
	//创建分布式锁
	mutex := rs.NewMutex(fmt.Sprintf("mutex_%s", req.OrderSn))
	if err := mutex.Lock(); err != nil {
		return nil, status.Errorf(codes.Internal, "创建分布式锁是吧")
	}
	//创建事务
	tx := global.DB.Begin()
	stockSellDetail := model.StockSellDetail{
		OrderSn: req.OrderSn,
		Status:  1,
	}
	var GoodsDetails []model.GoodsDetail
	for _, goodsInvInfo := range req.GoodsInfo {
		goodsId := goodsInvInfo.GoodsId
		num := goodsInvInfo.Num
		//查询库存信息
		var inventory model.Inventory
		if result := global.DB.Where(&model.Inventory{
			Goods: goodsId,
		}).First(&inventory); result.RowsAffected == 0 {
			zap.S().Info("找不到库存信息：{}", goodsId)
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "找不到库存信息")
		}
		//判断库存是否充足
		if inventory.Stocks < num {
			zap.S().Info("当前库存不足：{}", goodsId)
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "当前库存不足")
		}
		//扣减库存
		inventory.Stocks -= num
		if save := tx.Save(&inventory); save.RowsAffected == 0 { //这里用tx不是global.Db
			zap.S().Info("扣除库存操作失败：{}", goodsId)
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "扣除库存操作失败")
		}
		//保存扣除库存明细
		GoodsDetails = append(GoodsDetails, model.GoodsDetail{
			Goods: goodsId,
			Num:   num,
		})
	}
	stockSellDetail.Detail = GoodsDetails
	//保存库存扣除明细
	if result := tx.Create(&stockSellDetail); result.RowsAffected == 0 { //这里用tx不是global.Db
		zap.S().Errorf("保存库存扣减历史失败:%v\n", stockSellDetail.OrderSn)
		tx.Rollback()
		return nil, status.Errorf(codes.Internal, "保存库存扣减历史失败")
	}
	//释放分布式锁
	if ok, err := mutex.Unlock(); !ok || err != nil {
		zap.S().Errorf("释放分布式锁失败:%v\n", err)
		tx.Rollback()
		return nil, status.Errorf(codes.Internal, "释放分布式锁失败")
	}
	//提交
	tx.Commit()
	return &emptypb.Empty{}, nil
}

/*
*
归还库存
*/
func (c *InventoryServer) Reback(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {
	//创建事务
	tx := global.DB.Begin()
	for _, info := range req.GoodsInfo {
		//查询库存信息
		var inventory model.Inventory
		if result := global.DB.Where(&model.Inventory{Goods: info.GoodsId}).First(&inventory); result.RowsAffected == 0 {
			tx.Rollback() //事务回滚
			return nil, status.Errorf(codes.Internal, "找不到库存信息")
		}
		inventory.Stocks += info.Num
		tx.Save(&inventory)
	}
	//事务提交
	tx.Commit()
	return &emptypb.Empty{}, nil
}

/*
*
订单超时，获取归还库存的消息
*/
func AutoReback(ctx context.Context, msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
	type OrderInfo struct {
		OrderSn string
	}
	for _, msg := range msgs {
		//1 解析消息
		var orderInfo OrderInfo
		_ = json.Unmarshal(msg.Body, &orderInfo)
		zap.S().Infof("消费到库存回滚消息:%v", orderInfo)
		tx := global.DB.Begin()
		//2 获取是否存在已扣减库存的 记录，status=1
		var stockSellDetail model.StockSellDetail
		if res := tx.Model(&model.StockSellDetail{}).Where(&model.StockSellDetail{OrderSn: orderInfo.OrderSn, Status: 1}).First(&stockSellDetail); res.RowsAffected == 0 {
			//说明当前没有需要回归的库存
			tx.Rollback()
			return consumer.ConsumeSuccess, nil
		}
		//3 存在扣减记录则逐个归还库存
		zap.S().Infof("检测到库存记录需要回滚:%v", &stockSellDetail)
		detail := stockSellDetail.Detail
		for _, goodsDetail := range detail {
			if res := tx.Model(&model.Inventory{}).Where(&model.Inventory{Goods: goodsDetail.Goods}).Update("stocks", gorm.Expr("stocks+?", goodsDetail.Num)); res.RowsAffected == 0 {
				tx.Rollback()
				return consumer.ConsumeRetryLater, nil
			}
		}
		//4 原来的扣减记录状态改为2
		if res := tx.Model(&model.StockSellDetail{}).Where(&model.StockSellDetail{OrderSn: orderInfo.OrderSn}).Update("status", 2); res.RowsAffected == 0 {
			//说明当前没有需要回归的库存
			tx.Rollback()
			return consumer.ConsumeRetryLater, nil
		}
		tx.Commit()
		return consumer.ConsumeSuccess, nil
	}
	return consumer.ConsumeSuccess, nil
}
